// Created on: 2002-10-30
// Created by: Michael SAZONOV
// Copyright (c) 2002-2014 OPEN CASCADE SAS
//
// This file is part of Open CASCADE Technology software library.
//
// This library is free software; you can redistribute it and/or modify it under
// the terms of the GNU Lesser General Public License version 2.1 as published
// by the Free Software Foundation, with special exception defined in the file
// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
// distribution for complete text of the license and disclaimer of any warranty.
//
// Alternatively, this file may be used under the terms of Open CASCADE
// commercial license or contractual agreement.


#include <BinObjMgt_Persistent.hxx>
#include <BinObjMgt_Position.hxx>
#include <Standard_GUID.hxx>
#include <TDF_Data.hxx>
#include <TDF_Label.hxx>
#include <TDF_Tool.hxx>
#include <FSD_BinaryFile.hxx>

#define BP_INTSIZE         ((Standard_Integer)sizeof(Standard_Integer))
#define BP_EXTCHARSIZE     ((Standard_Integer)sizeof(Standard_ExtCharacter))
#define BP_REALSIZE        ((Standard_Integer)sizeof(Standard_Real))
#define BP_SHORTREALSIZE   ((Standard_Integer)sizeof(Standard_ShortReal))
#define BP_UUIDSIZE        ((Standard_Integer)sizeof(BinObjMgt_UUID))

// We define a GUID structure that is different from Standard_UUID because
// the latter contains a 'unsigned long' member that has variables size
// (4 or 8 bits) thus making the persistent files non-portable.
// This structure below ensures the portability.
typedef struct {
  unsigned int   Data1 ;        // 4-bytes long on all OS
  unsigned short Data2 ;        // 2-bytes long on all OS
  unsigned short Data3 ;        // 2-bytes long on all OS
  unsigned char  Data4[8] ;     // 8-bytes long on all OS
} BinObjMgt_UUID ;

//=======================================================================
//function : BinObjMgt_Persistent
//purpose  : Empty constructor
//=======================================================================

BinObjMgt_Persistent::BinObjMgt_Persistent ()
     : myIndex (1),
       myOffset(BP_HEADSIZE),
       mySize  (BP_HEADSIZE),
       myIsError (Standard_False),
       myOStream (NULL),
       myIStream (NULL),
       myDirectWritingIsEnabled (Standard_False)
{
  Init();
}

//=======================================================================
//function : Init
//purpose  : Initializes me to reuse again
//=======================================================================

void BinObjMgt_Persistent::Init ()
{
  if (myData.IsEmpty()) {
    Standard_Address aPiece = Standard::Allocate (BP_PIECESIZE);
    myData.Append (aPiece);
  }
  Standard_Integer *aData = (Standard_Integer*) myData(1);
  aData[0] = 0;         // Type Id
  aData[1] = 0;         // Object Id
  aData[2] = 0;         // Data length
  myIndex = 1;
  myOffset = BP_HEADSIZE;
  mySize = BP_HEADSIZE;
  myIsError = Standard_False;
  myDirectWritingIsEnabled = Standard_False;
}

//=======================================================================
//function : Write
//purpose  : Stores <me> to the stream.
//           inline Standard_OStream& operator<< (Standard_OStream&,
//           const BinObjMgt_Persistent&) is also available
//=======================================================================

Standard_OStream& BinObjMgt_Persistent::Write (Standard_OStream& theOS, const Standard_Boolean theDirectStream)
{
  if (myDirectWritingIsEnabled)
  { // if direct writing was enabled, everything is already written, just pass this stage
    myDirectWritingIsEnabled = Standard_False;
    return theOS;
  }
  Standard_Integer nbWritten = 0;
  Standard_Integer *aData = (Standard_Integer*) myData(1);
  // update data length
  aData[2] = mySize - BP_HEADSIZE;
  if (theDirectStream)
    aData[1] = -aData[1];
#if DO_INVERSE
  aData[0] = InverseInt (aData[0]);
  aData[1] = InverseInt (aData[1]);
  aData[2] = InverseInt (aData[2]);
#endif
  for (Standard_Integer i=1;
       theOS && nbWritten < mySize && i <= myData.Length();
       i++) {
    Standard_Integer nbToWrite = Min (mySize - nbWritten, BP_PIECESIZE);
    theOS.write ((char*)myData(i), nbToWrite);
    nbWritten += nbToWrite;
  }
  myIndex = 1;
  myOffset = BP_HEADSIZE;
  mySize = BP_HEADSIZE;
  myIsError = Standard_False;
  return theOS;
}

//=======================================================================
//function : Read
//purpose  : Retrieves <me> from the stream.
//           inline Standard_IStream& operator>> (Standard_IStream&,
//           BinObjMgt_Persistent&) is also available
//=======================================================================

Standard_IStream& BinObjMgt_Persistent::Read (Standard_IStream& theIS)
{
  myIndex = 1;
  myOffset = BP_HEADSIZE;
  mySize = BP_HEADSIZE;
  myIsError = Standard_False;

  Standard_Integer *aData = (Standard_Integer*) myData(1);
  aData[0] = 0;         // Type Id
  aData[1] = 0;         // Object Id
  aData[2] = 0;         // Data length

  // read TypeId
  theIS.read ((char*) &aData[0], BP_INTSIZE);
#ifdef DO_INVERSE
  aData[0] = InverseInt (aData[0]);
#endif
  if (theIS && aData[0] > 0) {
    // read Id and Length
    theIS.read ((char*)&aData[1], 2 * BP_INTSIZE);
#ifdef DO_INVERSE
    aData[1] = InverseInt (aData[1]);
    aData[2] = InverseInt (aData[2]);
#endif
    myDirectWritingIsEnabled = aData[1] < 0;
    if (myDirectWritingIsEnabled)
      aData[1] = -aData[1];
    if (theIS && aData[2] > 0) {
      mySize += aData[2];
      // read remaining data
      Standard_Integer nbRead = BP_HEADSIZE;
      for (Standard_Integer i=1;
           theIS && nbRead < mySize;
           i++) {
        if (i > myData.Length()) {
          // grow myData dynamically
          Standard_Address aPiece = Standard::Allocate (BP_PIECESIZE);
          myData.Append (aPiece);
        }
        Standard_Integer nbToRead = Min (mySize - nbRead, BP_PIECESIZE);
        char *ptr = (char*)myData(i);
        if (i == 1) {
          // 1st piece: reduce the number of bytes by header size
          ptr += BP_HEADSIZE;
          if (nbToRead == BP_PIECESIZE) nbToRead -= BP_HEADSIZE;
        }
        theIS.read (ptr, nbToRead);
        nbRead += nbToRead;
      }
    }
    else
      aData[2] = 0;
  }
  return theIS;
}

//=======================================================================
//function : Destroy
//purpose  : Frees the allocated memory
//=======================================================================

void BinObjMgt_Persistent::Destroy ()
{
  for (Standard_Integer i=1; i <= myData.Length(); i++) {
    Standard::Free (myData(i));
  }
  myData.Clear();
  myIndex = myOffset = mySize = 0;
}

//=======================================================================
//function : incrementData
//purpose  : Allocates theNbPieces more pieces
//=======================================================================

void BinObjMgt_Persistent::incrementData
  (const Standard_Integer theNbPieces)
{
  for (Standard_Integer i=1; i <= theNbPieces; i++) {
    Standard_Address aPiece = Standard::Allocate (BP_PIECESIZE);
    myData.Append (aPiece);
  }
}

//=======================================================================
//function : PutCharacter
//purpose  : 
//=======================================================================

BinObjMgt_Persistent& BinObjMgt_Persistent::PutCharacter
  (const Standard_Character theValue)
{
  alignOffset (1);
  prepareForPut (1);
  Standard_Character *aData = (Standard_Character*) myData(myIndex) + myOffset;
  *aData = theValue;
  myOffset++;
  return *this;
}

//=======================================================================
//function : PutByte
//purpose  : 
//=======================================================================

BinObjMgt_Persistent& BinObjMgt_Persistent::PutByte
  (const Standard_Byte theValue)
{
  alignOffset (1);
  prepareForPut (1);
  Standard_Byte *aData = (Standard_Byte*) myData(myIndex) + myOffset;
  *aData = theValue;
  myOffset++;
  return *this;
}

//=======================================================================
//function : PutExtCharacter
//purpose  : 
//=======================================================================

BinObjMgt_Persistent& BinObjMgt_Persistent::PutExtCharacter
  (const Standard_ExtCharacter theValue)
{
  alignOffset (BP_EXTCHARSIZE, Standard_True);
  prepareForPut (BP_EXTCHARSIZE);
  Standard_ExtCharacter *aData =
    (Standard_ExtCharacter*) ((char*)myData(myIndex) + myOffset);
#ifdef DO_INVERSE
  *aData = InverseExtChar (theValue);
#else
  *aData = theValue;
#endif
  myOffset += BP_EXTCHARSIZE;
  return *this;
}

//=======================================================================
//function : PutInteger
//purpose  : 
//=======================================================================

BinObjMgt_Persistent& BinObjMgt_Persistent::PutInteger
  (const Standard_Integer theValue)
{
  alignOffset (BP_INTSIZE, Standard_True);
  prepareForPut (BP_INTSIZE);
  Standard_Integer *aData = (Standard_Integer*) ((char*)myData(myIndex) + myOffset);
#ifdef DO_INVERSE
  *aData = InverseInt (theValue);
#else
  *aData = theValue;
#endif
  myOffset += BP_INTSIZE;
  return *this;
}

//=======================================================================
//function : PutReal
//purpose  : 
//=======================================================================

BinObjMgt_Persistent& BinObjMgt_Persistent::PutReal
  (const Standard_Real theValue)
{
  alignOffset (BP_INTSIZE, Standard_True);
  Standard_Integer nbPieces = prepareForPut (BP_REALSIZE);
  if (nbPieces > 0) {
    // the value intersects a piece boundary => go a long way
#ifdef DO_INVERSE
    Standard_Integer aStartIndex = myIndex;
    Standard_Integer aStartOffset = myOffset;
#endif
    putArray ((void*) &theValue, BP_REALSIZE);
#ifdef DO_INVERSE
    inverseRealData (aStartIndex, aStartOffset, BP_REALSIZE);
#endif
  }
  else {
    // the value fits in the current piece => put it quickly
    Standard_Real *aData = (Standard_Real*) ((char*)myData(myIndex) + myOffset);
#ifdef DO_INVERSE
    *aData = InverseReal (theValue);
#else
    *aData = theValue;
#endif
    myOffset += BP_REALSIZE;
  }
  return *this;
}

//=======================================================================
//function : PutShortReal
//purpose  : 
//=======================================================================

BinObjMgt_Persistent& BinObjMgt_Persistent::PutShortReal
  (const Standard_ShortReal theValue)
{
  alignOffset (BP_INTSIZE, Standard_True);
  prepareForPut (BP_SHORTREALSIZE);
  Standard_ShortReal *aData = (Standard_ShortReal*) ((char*)myData(myIndex) + myOffset);
#ifdef DO_INVERSE
  *aData = InverseShortReal (theValue);
#else
  *aData = theValue;
#endif
  myOffset += BP_SHORTREALSIZE;
  return *this;
}

//=======================================================================
//function : PutCString
//purpose  : Offset in output buffer is not aligned
//=======================================================================

BinObjMgt_Persistent& BinObjMgt_Persistent::PutCString
  (const Standard_CString theValue)
{
  alignOffset (1);
  Standard_Integer aSize = (Standard_Integer)(strlen (theValue) + 1);
  prepareForPut (aSize);
  putArray ((void* )theValue, aSize);
  return *this;
}

//=======================================================================
//function : PutAsciiString
//purpose  : Offset in output buffer is word-aligned
//=======================================================================

BinObjMgt_Persistent& BinObjMgt_Persistent::PutAsciiString
  (const TCollection_AsciiString& theValue)
{
  alignOffset (BP_INTSIZE, Standard_True);
  Standard_Integer aSize = theValue.Length() + 1;
  prepareForPut (aSize);
  putArray ((void*)theValue.ToCString(), aSize);
  return *this;
}

//=======================================================================
//function : PutExtendedString
//purpose  : Offset in output buffer is word-aligned
//=======================================================================

BinObjMgt_Persistent& BinObjMgt_Persistent::PutExtendedString
  (const TCollection_ExtendedString& theValue)
{
  alignOffset (BP_INTSIZE, Standard_True);
  Standard_Integer aSize = (theValue.Length() + 1) * BP_EXTCHARSIZE;
  prepareForPut (aSize);
#ifdef DO_INVERSE
  Standard_Integer aStartIndex = myIndex;
  Standard_Integer aStartOffset = myOffset;
#endif
  putArray ((void* )theValue.ToExtString(), aSize);
#ifdef DO_INVERSE
  inverseExtCharData (aStartIndex, aStartOffset, aSize - BP_EXTCHARSIZE);
#endif
  return *this;
}

//=======================================================================
//function : PutLabel
//purpose  : 
//=======================================================================

BinObjMgt_Persistent& BinObjMgt_Persistent::PutLabel
  (const TDF_Label& theValue)
{
  alignOffset (BP_INTSIZE, Standard_True);
  Standard_Integer aLen = (theValue.IsNull() ? 0 : theValue.Depth()+1);
  prepareForPut ((aLen + 1) * BP_INTSIZE);
  Standard_Integer *aData = (Standard_Integer*) ((char*)myData(myIndex) + myOffset);
  // store nb of tags
#ifdef DO_INVERSE
  *aData++ = InverseInt (aLen);
#else
  *aData++ = aLen;
#endif
  myOffset += BP_INTSIZE;
  if (!theValue.IsNull()) {
    TColStd_ListOfInteger aTagList;
    TDF_Tool::TagList (theValue, aTagList);
    TColStd_ListIteratorOfListOfInteger itTag(aTagList);
    for (; itTag.More(); itTag.Next()) {
      if (myOffset >= BP_PIECESIZE) {
        myOffset = 0;
        myIndex++;
        aData = (Standard_Integer*) ((char*)myData(myIndex) + myOffset);
      }
#ifdef DO_INVERSE
      *aData++ = InverseInt (itTag.Value());
#else
      *aData++ = itTag.Value();
#endif
      myOffset += BP_INTSIZE;
    }
  }
  return *this;
}

//=======================================================================
//function : PutGUID
//purpose  : 
//=======================================================================

BinObjMgt_Persistent& BinObjMgt_Persistent::PutGUID
  (const Standard_GUID& theValue)
{
  alignOffset (BP_INTSIZE, Standard_True);
  prepareForPut (BP_UUIDSIZE);
  const Standard_UUID aStandardUUID = theValue.ToUUID();
  BinObjMgt_UUID anUUID;
  anUUID.Data1 = (unsigned int)   aStandardUUID.Data1;
  anUUID.Data2 = (unsigned short) aStandardUUID.Data2;
  anUUID.Data3 = (unsigned short) aStandardUUID.Data3;
  anUUID.Data4[0] = aStandardUUID.Data4[0];
  anUUID.Data4[1] = aStandardUUID.Data4[1];
  anUUID.Data4[2] = aStandardUUID.Data4[2];
  anUUID.Data4[3] = aStandardUUID.Data4[3];
  anUUID.Data4[4] = aStandardUUID.Data4[4];
  anUUID.Data4[5] = aStandardUUID.Data4[5];
  anUUID.Data4[6] = aStandardUUID.Data4[6];
  anUUID.Data4[7] = aStandardUUID.Data4[7];
#ifdef DO_INVERSE
  anUUID.Data1 = (unsigned int)   InverseInt     (anUUID.Data1);
  anUUID.Data2 = (unsigned short) InverseExtChar (anUUID.Data2);
  anUUID.Data3 = (unsigned short) InverseExtChar (anUUID.Data3);
#endif
  putArray (&anUUID, BP_UUIDSIZE);
  return *this;
}

//=======================================================================
//function : PutCharArray
//purpose  : Put C array of char, theLength is the number of elements
//=======================================================================

BinObjMgt_Persistent& BinObjMgt_Persistent::PutCharArray
  (const BinObjMgt_PChar  theArray,
   const Standard_Integer theLength)
{
  alignOffset (1);
  prepareForPut (theLength);
  putArray (theArray, theLength);
  return *this;
}

//=======================================================================
//function : PutByteArray
//purpose  : Put C array of byte, theLength is the number of elements
//=======================================================================

BinObjMgt_Persistent& BinObjMgt_Persistent::PutByteArray
  (const BinObjMgt_PByte  theArray,
   const Standard_Integer theLength)
{
  alignOffset (1);
  prepareForPut (theLength);
  putArray (theArray, theLength);
  return *this;
}

//=======================================================================
//function : PutExtCharArray
//purpose  : Put C array of ExtCharacter, theLength is the number of elements
//=======================================================================

BinObjMgt_Persistent& BinObjMgt_Persistent::PutExtCharArray
  (const BinObjMgt_PExtChar theArray,
   const Standard_Integer   theLength)
{
  alignOffset (BP_EXTCHARSIZE, Standard_True);
  Standard_Integer aSize = theLength * BP_EXTCHARSIZE;
  prepareForPut (aSize);
#ifdef DO_INVERSE
  Standard_Integer aStartIndex = myIndex;
  Standard_Integer aStartOffset = myOffset;
#endif
  putArray (theArray, aSize);
#ifdef DO_INVERSE
  inverseExtCharData (aStartIndex, aStartOffset, aSize);
#endif
  return *this;
}

//=======================================================================
//function : PutIntArray
//purpose  : Put C array of int, theLength is the number of elements
//=======================================================================

BinObjMgt_Persistent& BinObjMgt_Persistent::PutIntArray
  (const BinObjMgt_PInteger theArray,
   const Standard_Integer   theLength)
{
  alignOffset (BP_INTSIZE, Standard_True);
  Standard_Integer aSize = theLength * BP_INTSIZE;
  prepareForPut (aSize);
#ifdef DO_INVERSE
  Standard_Integer aStartIndex = myIndex;
  Standard_Integer aStartOffset = myOffset;
#endif
  putArray (theArray, aSize);
#ifdef DO_INVERSE
  inverseIntData (aStartIndex, aStartOffset, aSize);
#endif
  return *this;
}

//=======================================================================
//function : PutRealArray
//purpose  : Put C array of double, theLength is the number of elements
//=======================================================================

BinObjMgt_Persistent& BinObjMgt_Persistent::PutRealArray
  (const BinObjMgt_PReal  theArray,
   const Standard_Integer theLength)
{
  alignOffset (BP_INTSIZE, Standard_True);
  Standard_Integer aSize = theLength * BP_REALSIZE;
  prepareForPut (aSize);
#ifdef DO_INVERSE
  Standard_Integer aStartIndex = myIndex;
  Standard_Integer aStartOffset = myOffset;
#endif
  putArray (theArray, aSize);
#ifdef DO_INVERSE
  inverseRealData (aStartIndex, aStartOffset, aSize);
#endif
  return *this;
}

//=======================================================================
//function : PutShortRealArray
//purpose  : Put C array of float, theLength is the number of elements
//=======================================================================

BinObjMgt_Persistent& BinObjMgt_Persistent::PutShortRealArray
  (const BinObjMgt_PShortReal theArray,
   const Standard_Integer     theLength)
{
  alignOffset (BP_INTSIZE, Standard_True);
  Standard_Integer aSize = theLength * BP_SHORTREALSIZE;
  prepareForPut (aSize);
#ifdef DO_INVERSE
  Standard_Integer aStartIndex = myIndex;
  Standard_Integer aStartOffset = myOffset;
#endif
  putArray (theArray, aSize);
#ifdef DO_INVERSE
  inverseShortRealData (aStartIndex, aStartOffset, aSize);
#endif
  return *this;
}

//=======================================================================
//function : GetCharacter
//purpose  : 
//=======================================================================

const BinObjMgt_Persistent& BinObjMgt_Persistent::GetCharacter
  (Standard_Character& theValue) const
{
  alignOffset (1);
  if (noMoreData (1)) return *this;
  Standard_Character *aData = (Standard_Character*) myData(myIndex) + myOffset;
  theValue = *aData;
  ((BinObjMgt_Persistent*)this)->myOffset++;
  return *this;
}

//=======================================================================
//function : GetByte
//purpose  : 
//=======================================================================

const BinObjMgt_Persistent& BinObjMgt_Persistent::GetByte
  (Standard_Byte& theValue) const
{
  alignOffset (1);
  if (noMoreData (1)) return *this;
  Standard_Byte *aData = (Standard_Byte*) myData(myIndex) + myOffset;
  theValue = *aData;
  ((BinObjMgt_Persistent*)this)->myOffset++;
  return *this;
}

//=======================================================================
//function : GetExtCharacter
//purpose  : 
//=======================================================================

const BinObjMgt_Persistent& BinObjMgt_Persistent::GetExtCharacter
  (Standard_ExtCharacter& theValue) const
{
  alignOffset (BP_EXTCHARSIZE);
  if (noMoreData (BP_EXTCHARSIZE)) return *this;
  Standard_ExtCharacter *aData =
    (Standard_ExtCharacter*) ((char*)myData(myIndex) + myOffset);
#ifdef DO_INVERSE
  theValue = InverseExtChar (*aData);
#else
  theValue = *aData;
#endif
  ((BinObjMgt_Persistent*)this)->myOffset += BP_EXTCHARSIZE;
  return *this;
}

//=======================================================================
//function : GetInteger
//purpose  : 
//=======================================================================

const BinObjMgt_Persistent& BinObjMgt_Persistent::GetInteger
  (Standard_Integer& theValue) const
{
  alignOffset (BP_INTSIZE);
  if (noMoreData (BP_INTSIZE)) return *this;
  Standard_Integer *aData = (Standard_Integer*) ((char*)myData(myIndex) + myOffset);
#ifdef DO_INVERSE
  theValue = InverseInt (*aData);
#else
  theValue = *aData;
#endif
  ((BinObjMgt_Persistent*)this)->myOffset += BP_INTSIZE;
  return *this;
}

//=======================================================================
//function : GetReal
//purpose  : 
//=======================================================================

const BinObjMgt_Persistent& BinObjMgt_Persistent::GetReal
  (Standard_Real& theValue) const
{
  alignOffset (BP_INTSIZE);
  if (noMoreData (BP_REALSIZE)) return *this;
  Standard_Integer nbPieces = (myOffset + BP_REALSIZE - 1) / BP_PIECESIZE;
  if (nbPieces > 0) {
    // the value intersects a piece boundary => go a long way
    getArray ((void*) &theValue, BP_REALSIZE);
  }
  else {
    // the value fits in the current piece => get it quickly
    Standard_Real *aData = (Standard_Real*) ((char*)myData(myIndex) + myOffset);
    theValue = *aData;
    ((BinObjMgt_Persistent*)this)->myOffset += BP_REALSIZE;
  }
#ifdef DO_INVERSE
  theValue = InverseReal (theValue);
#endif
  return *this;
}

//=======================================================================
//function : GetShortReal
//purpose  : 
//=======================================================================

const BinObjMgt_Persistent& BinObjMgt_Persistent::GetShortReal
  (Standard_ShortReal& theValue) const
{
  alignOffset (BP_INTSIZE);
  if (noMoreData (BP_SHORTREALSIZE)) return *this;
  Standard_ShortReal *aData = (Standard_ShortReal*) ((char*)myData(myIndex) + myOffset);
#ifdef DO_INVERSE
  theValue = InverseShortReal (*aData);
#else
  theValue = *aData;
#endif
  ((BinObjMgt_Persistent*)this)->myOffset += BP_SHORTREALSIZE;
  return *this;
}

//=======================================================================
//function : GetAsciiString
//purpose  : 
//=======================================================================

const BinObjMgt_Persistent& BinObjMgt_Persistent::GetAsciiString
  (TCollection_AsciiString& theValue) const
{
  alignOffset (BP_INTSIZE);
  Standard_Integer aStartIndex = myIndex;
  Standard_Integer aStartOffset = myOffset;
  BinObjMgt_Persistent* me = (BinObjMgt_Persistent*)this;
  char *aData = (char*) myData(myIndex) + myOffset;

  // count the string length
  while (!noMoreData (1) && *aData++) {
    me->myOffset++;
    if (myOffset >= BP_PIECESIZE) {
      me->myOffset = 0;
      aData = (char*) myData(++me->myIndex);
    }
  }
  if (IsError()) {
    me->myIndex = aStartIndex;
    me->myOffset = aStartOffset;
    return *this;
  }
  me->myOffset++;  // count the end null char

  if (myIndex == aStartIndex) {
    // all string is in one piece => simply copy
    theValue = aData - myOffset + aStartOffset;
  }
  else {
    // work through buffer string
    Standard_Integer aSize = (myIndex - aStartIndex) * BP_PIECESIZE +
      myOffset - aStartOffset;
    Standard_Address aString = Standard::Allocate (aSize);
    me->myIndex = aStartIndex;
    me->myOffset = aStartOffset;
    getArray (aString, aSize);
    theValue = (char*) aString;
    Standard::Free (aString);
  }

  return *this;
}

//=======================================================================
//function : GetExtendedString
//purpose  : 
//=======================================================================

const BinObjMgt_Persistent& BinObjMgt_Persistent::GetExtendedString
  (TCollection_ExtendedString& theValue) const
{
  alignOffset (BP_INTSIZE);
  Standard_Integer aStartIndex = myIndex;
  Standard_Integer aStartOffset = myOffset;
  BinObjMgt_Persistent* me = (BinObjMgt_Persistent*)this;
  Standard_ExtCharacter *aData =
    (Standard_ExtCharacter*) ((char*)myData(myIndex) + myOffset);

  // count the string length
  while (!noMoreData (1) && *aData++) {
    me->myOffset += BP_EXTCHARSIZE;
    if (myOffset >= BP_PIECESIZE) {
      me->myOffset = 0;
      aData = (Standard_ExtCharacter*) myData(++me->myIndex);
    }
  }
  if (IsError()) {
    me->myIndex = aStartIndex;
    me->myOffset = aStartOffset;
    return *this;
  }
  me->myOffset += BP_EXTCHARSIZE;  // count the end null char

  if (myIndex == aStartIndex) {
    // all string is in one piece => simply copy
    theValue = aData - (myOffset - aStartOffset) / BP_EXTCHARSIZE;
  }
  else {
    // work through buffer string
    Standard_Integer aSize = (myIndex - aStartIndex) * BP_PIECESIZE +
      (myOffset - aStartOffset);
    Standard_Address aString = Standard::Allocate (aSize);
    me->myIndex = aStartIndex;
    me->myOffset = aStartOffset;
    getArray (aString, aSize);
    theValue = (Standard_ExtCharacter*) aString;
    Standard::Free (aString);
  }
#ifdef DO_INVERSE
  Standard_PExtCharacter aString = (Standard_PExtCharacter)theValue.ToExtString();
  for (Standard_Integer i=0; i < theValue.Length(); i++)
    aString[i] = InverseExtChar (aString[i]);
#endif

  return *this;
}

//=======================================================================
//function : GetLabel
//purpose  : 
//=======================================================================

const BinObjMgt_Persistent& BinObjMgt_Persistent::GetLabel
  (const Handle(TDF_Data)& theDS,
   TDF_Label& theValue) const
{
  theValue.Nullify();
  alignOffset (BP_INTSIZE);
  if (noMoreData (BP_INTSIZE)) return *this;
  BinObjMgt_Persistent* me = (BinObjMgt_Persistent*)this;
  // retrieve nb of tags
  Standard_Integer *aData = (Standard_Integer*) ((char*)myData(myIndex) + myOffset);
  Standard_Integer aLen = *aData++;
#ifdef DO_INVERSE
  aLen = InverseInt (aLen);
#endif
  me->myOffset += BP_INTSIZE;
  if (noMoreData (aLen * BP_INTSIZE)) return *this;

  if (aLen > 0) {
    // retrieve tag list
    TColStd_ListOfInteger aTagList;
    while (aLen > 0) {
      if (myOffset >= BP_PIECESIZE) {
        me->myOffset = 0;
        me->myIndex++;
        aData = (Standard_Integer*) ((char*)myData(myIndex) + myOffset);
      }
#ifdef DO_INVERSE
      aTagList.Append (InverseInt (*aData++));
#else
      aTagList.Append (*aData++);
#endif
      me->myOffset += BP_INTSIZE;
      aLen--;
    }
    // find label by entry
    TDF_Tool::Label (theDS, aTagList, theValue, Standard_True);
  }
  return *this;
}

//=======================================================================
//function : GetGUID
//purpose  : 
//=======================================================================

const BinObjMgt_Persistent& BinObjMgt_Persistent::GetGUID
  (Standard_GUID& theValue) const
{
  alignOffset (BP_INTSIZE);
  if (noMoreData (BP_UUIDSIZE))
    return *this;
  BinObjMgt_UUID anUUID;
  getArray (&anUUID, BP_UUIDSIZE);
#ifdef DO_INVERSE
  anUUID.Data1 = (unsigned int)   InverseInt     (anUUID.Data1);
  anUUID.Data2 = (unsigned short) InverseExtChar (anUUID.Data2);
  anUUID.Data3 = (unsigned short) InverseExtChar (anUUID.Data3);
#endif
  theValue = Standard_GUID (anUUID.Data1, anUUID.Data2, anUUID.Data3,
                            ((anUUID.Data4[0] << 8) | (anUUID.Data4[1])),
                            anUUID.Data4[2], anUUID.Data4[3], anUUID.Data4[4],
                            anUUID.Data4[5], anUUID.Data4[6], anUUID.Data4[7]);
  return *this;
}

//=======================================================================
//function : GetCharArray
//purpose  : Get C array of char, theLength is the number of elements;
//           theArray must point to a 
//           space enough to place theLength elements
//=======================================================================

const BinObjMgt_Persistent& BinObjMgt_Persistent::GetCharArray
  (const BinObjMgt_PChar  theArray,
   const Standard_Integer theLength) const
{
  alignOffset (1);
  if (noMoreData (theLength)) return *this;
  getArray (theArray, theLength);
  return *this;
}

//=======================================================================
//function : GetByteArray
//purpose  : Get C array of unsigned chars, theLength is the number of elements;
//           theArray must point to a 
//           space enough to place theLength elements
//=======================================================================

const BinObjMgt_Persistent& BinObjMgt_Persistent::GetByteArray
  (const BinObjMgt_PByte  theArray,
   const Standard_Integer theLength) const
{
  alignOffset (1);
  if (noMoreData (theLength)) return *this;
  getArray (theArray, theLength);
  return *this;
}

//=======================================================================
//function : GetExtCharArray
//purpose  : Get C array of ExtCharacter, theLength is the number of elements;
//           theArray must point to a 
//           space enough to place theLength elements
//=======================================================================

const BinObjMgt_Persistent& BinObjMgt_Persistent::GetExtCharArray
  (const BinObjMgt_PExtChar theArray,
   const Standard_Integer   theLength) const
{
  alignOffset (BP_EXTCHARSIZE, Standard_True);
  Standard_Integer aSize = theLength * BP_EXTCHARSIZE;
  if (noMoreData (aSize)) return *this;
  getArray (theArray, aSize);
#ifdef DO_INVERSE
  for (Standard_Integer i=0; i < theLength; i++)
    theArray[i] = InverseExtChar (theArray[i]);
#endif
  return *this;
}

//=======================================================================
//function : GetIntArray
//purpose  : Get C array of int, theLength is the number of elements;
//           theArray must point to a 
//           space enough to place theLength elements
//=======================================================================

const BinObjMgt_Persistent& BinObjMgt_Persistent::GetIntArray
  (const BinObjMgt_PInteger theArray,
   const Standard_Integer   theLength) const
{
  alignOffset (BP_INTSIZE, Standard_True);
  Standard_Integer aSize = theLength * BP_INTSIZE;
  if (noMoreData (aSize)) return *this;
  getArray (theArray, aSize);
#ifdef DO_INVERSE
  for (Standard_Integer i=0; i < theLength; i++)
    theArray[i] = InverseInt (theArray[i]);
#endif
  return *this;
}

//=======================================================================
//function : GetRealArray
//purpose  : Get C array of double, theLength is the number of elements;
//           theArray must point to a 
//           space enough to place theLength elements
//=======================================================================

const BinObjMgt_Persistent& BinObjMgt_Persistent::GetRealArray
  (const BinObjMgt_PReal  theArray,
   const Standard_Integer theLength) const
{
  alignOffset (BP_INTSIZE, Standard_True);
  Standard_Integer aSize = theLength * BP_REALSIZE;
  if (noMoreData (aSize)) return *this;
  getArray (theArray, aSize);
#ifdef DO_INVERSE
  for (Standard_Integer i=0; i < theLength; i++)
    theArray[i] = InverseReal (theArray[i]);
#endif
  return *this;
}

//=======================================================================
//function : GetShortRealArray
//purpose  : Get C array of float, theLength is the number of elements;
//           theArray must point to a 
//           space enough to place theLength elements
//=======================================================================

const BinObjMgt_Persistent& BinObjMgt_Persistent::GetShortRealArray
  (const BinObjMgt_PShortReal theArray,
   const Standard_Integer     theLength) const
{
  alignOffset (BP_INTSIZE, Standard_True);
  Standard_Integer aSize = theLength * BP_SHORTREALSIZE;
  if (noMoreData (aSize)) return *this;
  getArray (theArray, aSize);
#ifdef DO_INVERSE
  for (Standard_Integer i=0; i < theLength; i++)
    theArray[i] = InverseShortReal (theArray[i]);
#endif
  return *this;
}

//=======================================================================
//function : putArray
//purpose  : Puts theSize bytes from theArray
//=======================================================================

void BinObjMgt_Persistent::putArray
  (const Standard_Address theArray,
   const Standard_Integer theSize)
{
  char *aPtr = (char*) theArray;
  Standard_Integer aLen = theSize;
  while (aLen > 0) {
    if (myOffset >= BP_PIECESIZE) {
      myIndex++;
      myOffset = 0;
    }
    Standard_Integer aLenInPiece = Min (aLen, BP_PIECESIZE - myOffset);
    char *aData = (char*) myData(myIndex) + myOffset;
    memcpy (aData, aPtr, aLenInPiece);
    aLen -= aLenInPiece;
    aPtr += aLenInPiece;
    myOffset += aLenInPiece;
  }
}

//=======================================================================
//function : getArray
//purpose  : Gets theLength bytes into theArray
//=======================================================================

void BinObjMgt_Persistent::getArray
  (const Standard_Address theArray,
   const Standard_Integer theSize) const
{
  char *aPtr = (char*) theArray;
  Standard_Integer aLen = theSize;
  BinObjMgt_Persistent *me = (BinObjMgt_Persistent*) this;
  while (aLen > 0) {
    if (myOffset >= BP_PIECESIZE) {
      me->myIndex++;
      me->myOffset = 0;
    }
    Standard_Integer aLenInPiece = Min (aLen, BP_PIECESIZE - myOffset);
    char *aData = (char*) myData(myIndex) + myOffset;
    memcpy (aPtr, aData, aLenInPiece);
    aLen -= aLenInPiece;
    aPtr += aLenInPiece;
    me->myOffset += aLenInPiece;
  }
}

//=======================================================================
//function : inverseExtCharData
//purpose  : Inverses bytes in the data addressed by the given values
//=======================================================================

void BinObjMgt_Persistent::inverseExtCharData
  (const Standard_Integer theIndex,
   const Standard_Integer theOffset,
   const Standard_Integer theSize)
{
  Standard_Integer anIndex = theIndex;
  Standard_Integer anOffset = theOffset;
  Standard_Integer aLen = theSize;
  while (aLen > 0) {
    Standard_Integer aLenInPiece = Min (aLen, BP_PIECESIZE - anOffset);
    Standard_ExtCharacter *aData = (Standard_ExtCharacter*)
      ( (char*) myData(anIndex) + anOffset);
    for (Standard_Integer i=0; i < aLenInPiece / BP_EXTCHARSIZE; i++)
      aData[i] = FSD_BinaryFile::InverseExtChar (aData[i]);
    aLen -= aLenInPiece;
    anOffset += aLenInPiece;
    if (anOffset >= BP_PIECESIZE) {
      anIndex++;
      anOffset = 0;
    }
  }
}

//=======================================================================
//function : inverseIntData
//purpose  : Inverses bytes in the data addressed by the given values
//=======================================================================

void BinObjMgt_Persistent::inverseIntData
  (const Standard_Integer theIndex,
   const Standard_Integer theOffset,
   const Standard_Integer theSize)
{
  Standard_Integer anIndex = theIndex;
  Standard_Integer anOffset = theOffset;
  Standard_Integer aLen = theSize;
  while (aLen > 0) {
    Standard_Integer aLenInPiece = Min (aLen, BP_PIECESIZE - anOffset);
    Standard_Integer *aData = (Standard_Integer*) ((char*)myData(anIndex) + anOffset);
    for (Standard_Integer i=0; i < aLenInPiece / BP_INTSIZE; i++)
      aData[i] = FSD_BinaryFile::InverseInt (aData[i]);
    aLen -= aLenInPiece;
    anOffset += aLenInPiece;
    if (anOffset >= BP_PIECESIZE) {
      anIndex++;
      anOffset = 0;
    }
  }
}

//=======================================================================
//function : inverseRealData
//purpose  : Inverses bytes in the data addressed by the given values
//=======================================================================

void BinObjMgt_Persistent::inverseRealData
  (const Standard_Integer theIndex,
   const Standard_Integer theOffset,
   const Standard_Integer theSize)
{
  Standard_Integer anIndex = theIndex;
  Standard_Integer anOffset = theOffset;
  Standard_Integer aLen = theSize;

  union {
        Standard_Real*    aRealData;
        Standard_Integer* aIntData;
      } aWrapUnion;

  void *aPrevPtr = 0;
  while (aLen > 0) {
    Standard_Integer aLenInPiece = Min (aLen, BP_PIECESIZE - anOffset);

    aWrapUnion.aRealData = (Standard_Real*) ((char*)myData(anIndex) + anOffset);
    
    if (aPrevPtr) {
      Standard_Integer aTmp;
      aTmp = FSD_BinaryFile::InverseInt (*(Standard_Integer*)aPrevPtr);
      *(Standard_Integer*)aPrevPtr = FSD_BinaryFile::InverseInt (*aWrapUnion.aIntData);
      *aWrapUnion.aIntData = aTmp;
      aWrapUnion.aIntData++;
      aPrevPtr = 0;
    }
    for (Standard_Integer i=0; i < aLenInPiece / BP_REALSIZE; i++)
      aWrapUnion.aRealData[i] = FSD_BinaryFile::InverseReal(aWrapUnion.aRealData[i]);
    if (aLenInPiece % BP_REALSIZE)
      aPrevPtr = &aWrapUnion.aRealData[aLenInPiece / BP_REALSIZE];
    aLen -= aLenInPiece;
    anOffset += aLenInPiece;
    if (anOffset >= BP_PIECESIZE) {
      anIndex++;
      anOffset = 0;
    }
  }
}

//=======================================================================
//function : inverseShortRealData
//purpose  : Inverses bytes in the data addressed by the given values
//=======================================================================

void BinObjMgt_Persistent::inverseShortRealData
  (const Standard_Integer theIndex,
   const Standard_Integer theOffset,
   const Standard_Integer theSize)
{
  Standard_Integer anIndex = theIndex;
  Standard_Integer anOffset = theOffset;
  Standard_Integer aLen = theSize;
  while (aLen > 0) {
    Standard_Integer aLenInPiece = Min (aLen, BP_PIECESIZE - anOffset);
    Standard_ShortReal *aData =
      (Standard_ShortReal *) ((char *)myData(anIndex) + anOffset);
    for (Standard_Integer i=0; i < aLenInPiece / BP_INTSIZE; i++)
      aData[i] = FSD_BinaryFile::InverseShortReal (aData[i]);
    aLen -= aLenInPiece;
    anOffset += aLenInPiece;
    if (anOffset >= BP_PIECESIZE) {
      anIndex++;
      anOffset = 0;
    }
  }
}

//=======================================================================
//function : GetOStream
//purpose  : Gets the stream for and enables direct writing
//=======================================================================

Standard_OStream* BinObjMgt_Persistent::GetOStream()
{
  Write (*myOStream, Standard_True); // finishes already stored data save
  myStreamStart = new BinObjMgt_Position (*myOStream);
  myStreamStart->WriteSize (*myOStream, Standard_True);
  myDirectWritingIsEnabled = Standard_True;
  return myOStream;
}

//=======================================================================
//function : GetOStream
//purpose  : Gets the stream for and enables direct writing
//=======================================================================
Standard_IStream* BinObjMgt_Persistent::GetIStream()
{
  // skip the stream size first
  myIStream->seekg (sizeof (uint64_t), std::ios_base::cur);
  return myIStream;
}
